import domLoaded from 'dom-loaded';
import select from 'select-dom';
import mobile from 'is-mobile';
import { observe } from 'selector-observer';
import { getIconClass, type ColorMode } from 'atom-file-icons';

import { StorageKey } from './background';
import '../css/icons.css';

let colorsDisabled = false;
let darkMode = false;
let siteDarkMode = false;

const enum Host {
  GitHub = 'github',
  GitLab = 'gitlab',
  GitCode = 'gitcode',
  Others = 'others',
}

const fonts = [
  { name: 'FontAwesome', path: 'fonts/fontawesome.woff2' },
  { name: 'Mfizz', path: 'fonts/mfixx.woff2' },
  { name: 'Devicons', path: 'fonts/devopicons.woff2' },
  { name: 'file-icons', path: 'fonts/file-icons.woff2' },
  { name: 'Octicons Regular', path: 'fonts/octicons.woff2' },
];

const isGithubPage = () => /.*github.*/.test(window.location.hostname);

const isGithubTreeViewPage = () => {
  if (!isGithubPage()) return false;

  return !!document.querySelector('#repos-file-tree');
};

const isGithubFilesPage = () => {
  if (!isGithubPage()) return false;

  const pathname = window.location.pathname;
  const filesPageUrlPattern = new RegExp(/^\/.+\/.+\/pull\/\d+\/files$/);
  return pathname.match(filesPageUrlPattern) ? true : false;
};

const getSelector = (hostname: string) => {
  switch (true) {
    case /.*github.*/.test(hostname):
      // if it is a github pull request files page
      if (isGithubFilesPage()) {
        return {
          filenameSelector:
            'ul.ActionList>li[id^=file-tree-item-diff-][role=treeitem]>a>span:nth-child(2)',
          iconSelector:
            'ul.ActionList>li[id^=file-tree-item-diff-][role=treeitem]>a>span:first-child',
          host: Host.GitHub,
        };
      }

      if (isGithubTreeViewPage()) {
        return {
          filenameSelector:
            'div.PRIVATE_TreeView-item-content > span.PRIVATE_TreeView-item-content-text > span',
          iconSelector:
            'div.PRIVATE_TreeView-item-content > div.PRIVATE_TreeView-item-visual',
          host: Host.GitHub,
        };
      }

      return {
        filenameSelector:
          'tr.js-navigation-item > td.content > span, .files-list > a.list-item, div.js-navigation-item > div[role="rowheader"] > span',
        iconSelector:
          'tr.js-navigation-item > td.icon, .files-list > a.list-item, div.js-navigation-item > div[role="gridcell"]:first-child',
        host: Host.GitHub,
      };
    case /.*gitlab.*/.test(hostname):
      return {
        filenameSelector: 'tr.tree-item > td.tree-item-file-name > a > span',
        iconSelector: 'tr.tree-item > td.tree-item-file-name > i',
        host: Host.GitLab,
      };
    case /.*gitcode.*/.test(hostname):
      return {
        filenameSelector: 'table tbody tr td:first-child a, table tbody tr td .file-name, table tbody tr td a[title], tr td:first-child a',
        iconSelector: 'table tbody tr td:first-child svg, table tbody tr td:first-child .file-icon, tr td:first-child svg, tr td:first-child .icon',
        host: Host.GitCode,
      };
    default:
      return {
        filenameSelector: 'tr > td.name > a',
        iconSelector: 'tr > td.name > span',
        host: Host.Others,
      };
  }
};

const { filenameSelector, iconSelector, host } = getSelector(
  window.location.hostname,
);
const isMobile = mobile();
const isGitHub = host === Host.GitHub;
const isGitCode = host === Host.GitCode;

const loadFonts = () => {
  fonts.forEach((font) => {
    const fontFace = new FontFace(
      font.name,
      `url("${chrome.runtime.getURL(font.path)}") format("woff2")`,
      {
        style: 'normal',
        weight: 'normal',
      },
    );

    fontFace
      .load()
      .then((loadedFontFace) => document.fonts.add(loadedFontFace));
  });
};

const getGitHubMobileFilename = (filenameDom: HTMLElement) =>
  Array.from(filenameDom.childNodes)
    .filter((node) => node.nodeType === node.TEXT_NODE)
    .map((node) => node.nodeValue?.trim() || '')
    .join('');

const replaceIcon = ({
  iconDom,
  filenameDom,
}: {
  iconDom: HTMLElement | null;
  filenameDom: HTMLElement;
}) => {
  let filename = '';

  if (isGitHub && isMobile) {
    filename = getGitHubMobileFilename(filenameDom);
  } else if (isGitCode) {
    // Enhanced filename extraction for GitCode
    filename = filenameDom.textContent?.trim() ||
               filenameDom.getAttribute('title')?.trim() ||
               filenameDom.getAttribute('data-name')?.trim() || '';

    // If still no filename, try to find it in child elements
    if (!filename) {
      const textNode = filenameDom.querySelector('.file-name, .name, span:last-child');
      if (textNode) {
        filename = textNode.textContent?.trim() || '';
      }
    }
  } else {
    filename = filenameDom.textContent?.trim() ?? '';
  }

  // Skip if no filename found
  if (!filename) {
    console.log('GitCode: No filename found for element', filenameDom);
    return;
  }

  // Skip directory icons
  if (iconDom && (
    iconDom.classList.contains('octicon-file-directory') ||
    iconDom.classList.contains('directory') ||
    iconDom.classList.contains('folder')
  )) {
    return;
  }

  const getIconColorMode = (): ColorMode => {
    if (colorsDisabled) {
      return 'mono';
    }

    if (siteDarkMode) {
      return 'dark';
    }

    return 'light';
  };

  const className = getIconClass(filename, {
    colorMode: getIconColorMode(),
    skipFallback: true,
  });

  if (!className) {
    console.log('GitCode: No icon class found for filename:', filename);
    return;
  }

  const darkClassName = darkMode ? 'dark' : '';

  if (className) {
    const icon = document.createElement('span');

    if (isGitHub) {
      icon.className = `icon octicon-file ${className} ${darkClassName}`;
    } else if (isGitCode) {
      icon.className = `${className} ${darkClassName}`;
      icon.style.marginRight = '4px';
      icon.style.fontSize = '16px';
      icon.style.lineHeight = '1';
      icon.style.display = 'inline-block';
      icon.style.verticalAlign = 'middle';
    } else {
      icon.className = `${className} ${darkClassName}`;
      icon.style.marginRight = '3px';
    }

    if (iconDom?.parentNode) {
      iconDom.parentNode.replaceChild(icon, iconDom as HTMLElement);
      console.log('GitCode: Replaced icon for', filename, 'with class', className);
    } else {
      console.log('GitCode: No parent node found for icon replacement');
    }
  }
};

const update = () => {
  const filenameDoms = select.all(filenameSelector);
  const iconDoms = select.all(iconSelector);

  for (let i = 0; i < filenameDoms.length; i += 1) {
    replaceIcon({
      iconDom: iconDoms[i],
      filenameDom: filenameDoms[i],
    });
  }
};

const replaceGithubFileIcons = (
  triggerSelector: string,
  fileSelector: string,
  iconSelector = '.octicon-file',
) => {
  observe(triggerSelector, {
    add(element) {
      const filenameDom = select(fileSelector, element);
      if (filenameDom) {
        const iconDom = select(iconSelector, element);
        if (iconDom) {
          replaceIcon({ iconDom, filenameDom });
        }
      }
    },
  });
};

const init = async () => {
  loadFonts();

  await domLoaded;

  if (isGitHub) {
    siteDarkMode =
      document.querySelector('html')?.getAttribute('data-color-mode') ===
      'dark';

    if (isGithubFilesPage()) {
      replaceGithubFileIcons(
        'ul.ActionList > li[id^=file-tree-item-diff-][role=treeitem]',
        'a > span:nth-child(2)',
      );
    } else {
      replaceGithubFileIcons(
        '.js-navigation-container > .js-navigation-item',
        'div[role="rowheader"] > span',
      );

      replaceGithubFileIcons(
        '.PRIVATE_TreeView-item-content:has(span.PRIVATE_TreeView-item-content-text > span:not([class]))',
        'span.PRIVATE_TreeView-item-content-text',
      );

      replaceGithubFileIcons(
        '.react-directory-filename-column',
        '.react-directory-truncate',
        'svg:not(.icon-directory)',
      );
    }
  } else if (isGitCode) {
    // Enhanced GitCode dark mode detection
    const detectGitCodeDarkMode = () => {
      const html = document.documentElement;
      const body = document.body;
      const bodyBg = window.getComputedStyle(body).backgroundColor;
      const htmlBg = window.getComputedStyle(html).backgroundColor;

      // GitCode uses data-theme="black" for dark mode and data-theme="light" for light mode
      const htmlTheme = html.getAttribute('data-theme');
      const bodyTheme = body.getAttribute('data-theme');

      return (
        htmlTheme === 'black' ||
        bodyTheme === 'black' ||
        html.classList.contains('dark') ||
        body.classList.contains('dark') ||
        html.classList.contains('theme-dark') ||
        body.classList.contains('theme-dark') ||
        (html.getAttribute('data-theme') || '') === 'dark' ||
        (body.getAttribute('data-theme') || '') === 'dark' ||
        bodyBg.includes('rgb(13, 17, 23)') ||
        bodyBg.includes('rgb(22, 27, 34)') ||
        bodyBg.includes('rgb(36, 41, 47)') ||
        htmlBg.includes('rgb(13, 17, 23)') ||
        htmlBg.includes('rgb(22, 27, 34)') ||
        htmlBg.includes('rgb(36, 41, 47)') ||
        // Check for dark background colors
        (bodyBg.startsWith('rgb(') &&
         bodyBg.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/) &&
         parseInt((bodyBg.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/) || [])[1] || '255') < 50)
      );
    };

    siteDarkMode = detectGitCodeDarkMode() || false;

    // Enhanced GitCode update function with better element detection
    const updateGitCode = () => {
      // Re-detect dark mode on each update
      siteDarkMode = detectGitCodeDarkMode() || false;

      const filenameDoms = select.all(filenameSelector);
      const iconDoms = select.all(iconSelector);

      console.log('GitCode update - Found elements:', {
        filenames: filenameDoms.length,
        icons: iconDoms.length,
        darkMode: siteDarkMode
      });

      for (let i = 0; i < filenameDoms.length; i += 1) {
        replaceIcon({
          iconDom: iconDoms[i],
          filenameDom: filenameDoms[i],
        });
      }
    };

    updateGitCode();

    // Multiple event listeners for GitCode page changes
    document.addEventListener('pjax:end', updateGitCode);
    document.addEventListener('DOMContentLoaded', updateGitCode);

    // Observer for dynamic content changes
    const observer = new MutationObserver((mutations) => {
      let shouldUpdate = false;
      mutations.forEach((mutation) => {
        if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
          // Check if new table rows or file elements were added
          mutation.addedNodes.forEach((node) => {
            if (node.nodeType === Node.ELEMENT_NODE) {
              const element = node as Element;
              if (element.matches('tr, table, tbody') ||
                  element.querySelector('tr, table, tbody')) {
                shouldUpdate = true;
              }
            }
          });
        }

        // Watch for data-theme attribute changes on html element
        if (mutation.type === 'attributes' &&
            mutation.attributeName === 'data-theme' &&
            mutation.target === document.documentElement) {
          console.log('GitCode: Theme changed to', document.documentElement.getAttribute('data-theme'));
          shouldUpdate = true;
        }
      });

      if (shouldUpdate) {
        setTimeout(updateGitCode, 100); // Small delay to ensure DOM is ready
      }
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true
    });

    // Also observe the html element for data-theme attribute changes
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['data-theme']
    });

    // Listen for custom theme change events from background script
    window.addEventListener('gitcode-theme-changed', (event: Event) => {
      const customEvent = event as CustomEvent;
      console.log('GitCode: Received theme change event', customEvent.detail);
      const newTheme = customEvent.detail.theme;

      // Update extension's darkMode setting to match
      chrome.storage.sync.set({ darkMode: newTheme === 'black' }, () => {
        console.log('GitCode: Updated extension darkMode to', newTheme === 'black');

        // Force update icons with new theme
        setTimeout(updateGitCode, 50);
      });
    });
  } else {
    update();
    document.addEventListener('pjax:end', update);
  }
};

chrome.storage.sync.get(
  [StorageKey.ColorsDisabled, StorageKey.DarkMode],
  (result) => {
    colorsDisabled =
      result.colorsDisabled === undefined
        ? colorsDisabled
        : result.colorsDisabled;

    darkMode = result.darkMode === undefined ? darkMode : result.darkMode;

    init();
  },
);

chrome.runtime.onMessage.addListener(function (request) {
  if (request.message === 'file-icon-extension-page-update') {
    // reinitialize after page update
    init();
  }
});
